<?php

namespace backend\components\adapter;

use Yii;
use yii\helpers\FileHelper;
use backend\models\Clients;
use backend\models\Extensions;
use backend\models\Languages;
use backend\components\InstallerAdapter;
use common\components\Helper;

/**
 * Language installer
 *
 * @author loong
 */
class LanguageAdapter extends InstallerAdapter
{
    /**
     * @var bool Core language pack flag
     */
    protected $core = false;

    /**
     * Custom install method
     * @return bool|void
     * @throws \Throwable
     * @throws \yii\base\ErrorException
     * @throws \yii\db\Exception
     */
    public function install()
    {
        $clientName = strtolower((string)$this->manifest->attributes()->client);
        $client = Clients::findOne(['name' => $clientName]);
        if (is_null($client)) {
            $detail = Yii::t('installer', 'ERROR_UNKNOWN_CLIENT', $clientName);
            $msg = Yii::t('installer', 'ABORT_LANGUAGE', $detail);
            $this->parent->abort($msg);
            return false;
        }
        $basePath = Yii::getAlias('@root' . $client->path);
        $clientId = $client->id;
        $element = $this->manifest->files;
        return $this->_install($clientName, $basePath, $clientId, $element);
    }

    /**
     * Install function that is designed to handle individual clients
     * @param string $clientName The client name
     * @param string $basePath The base name
     * @param int $clientId The client id
     * @param object $element The XML element
     * @return bool|int The extension ID on success, boolean false on failure
     * @throws \Throwable
     * @throws \yii\base\ErrorException
     * @throws \yii\base\Exception
     * @throws \yii\db\Exception
     */
    protected function _install($clientName, $basePath, $clientId, &$element)
    {
        $tag = (string)$this->manifest->tag;
        if (!$tag) {
            $detail = Yii::t('installer', 'ERROR_NO_LANGUAGE_TAG');
            $msg = Yii::t('installer', 'ABORT_LANGUAGE', $detail);
            $this->parent->abort($msg);
            return false;
        }
        $this->set('tag', $tag);

        $this->parent->setPath('extension_' . $clientName, $basePath . '/messages/' . $tag);

        $created = false;
        if (!file_exists($this->parent->getPath('extension_' . $clientName))) {
            $created = FileHelper::createDirectory($this->parent->getPath('extension_' . $clientName));
            if (!$created) {
                $path = Helper::getRelativePath($this->parent->getPath('extension_' . $clientName));
                $detail = Yii::t('installer', 'ERROR_CREATE_FOLDER_FAILED', $path);
                $msg = Yii::t('installer', 'ABORT_LANGUAGE', $detail);
                $this->parent->abort($msg);
                return false;
            }
        } else {
            $updateElement = $this->manifest->update;
            if ($updateElement || $this->parent->isUpgrade()) {
                return $this->update();
            } else {
                $messagesPath = $this->parent->getPath('extension_' . $clientName);
                $paramMsg = Yii::t('installer', 'ERROR_FOLDER_IN_USE', Helper::getRelativePath($messagesPath));
                Yii::$app->session->addFlash('warning', Yii::t('installer', 'ABORT_LANGUAGE', $paramMsg));
                return false;
            }
        }

        /*
		 * If we created the language directory we will want to remove it if we
		 * have to roll back the installation, so let's add it to the installation
		 * step stack
		 */
        if ($created) {
            $this->parent->pushStep(['type' => 'folder', 'path' => $this->parent->getPath('extension_' . $clientName)]);
        }

        // Copy all the necessary files
        if ($this->parent->parseFiles($element, $clientName) === false) {
            // Install failed, rollback changes
            $this->parent->abort();
            return false;
        }
        $this->extension->name = (string)$this->manifest->name;
        $this->extension->type = 'language';
        $this->extension->element = (string)$this->manifest->tag;
        $this->extension->folder = '';
        $this->extension->client_id = $clientId;
        $this->extension->enabled = 1;
        $this->extension->protected = 0;
        $this->extension->manifest_cache = $this->parent->generateManifestCache();
        $this->extension->params = $this->parent->getParams();
        $this->extension->status = 1;
        if (!$this->extension->save()) {
            $errors = $this->parent->errorsToString($this->extension->getErrors());
            $this->parent->abort(Yii::t('installer', 'ABORT_LANGUAGE', $errors));
        }
        if ($clientName === Helper::getClient()) {
            // Prepare language data for store.
            $languages = Languages::findOne(['code' => (string)$this->manifest->tag]);
            if (is_null($languages)) {
                $languages = new Languages();
                $languages->code = (string)$this->manifest->tag;
                $languages->title = (string)$this->manifest->name;
                $languages->title_native = (string)$this->manifest->metadata->nativeName;
                $languages->sef = $this->getSefString($this->manifest->tag);
                $languages->image = strtolower(str_replace('-', '_', (string)$this->manifest->tag));
                $languages->description = '';
                $languages->metakey = '';
                $languages->metadesc = '';
                $languages->sitename = '';
                $languages->published = 1;
                $languages->ordering = 0;
                if (!$languages->save()) {
                    $sourceMsg = 'WARNING_UNABLE_TO_INSTALL_CONTENT_LANGUAGE';
                    $errorMsg = Yii::t('installer', $sourceMsg, [
                        'name' => $this->manifest->name,
                        'error' => implode('&emsp;&emsp;<br>', $languages->getFirstErrors())
                    ]);
                    Yii::$app->session->addFlash('error', $errorMsg);
                }
            }
        }
        return $this->extension->id;
    }

    /**
     * 卸载语言扩展
     * @param object $extension 语言扩展数据对象
     * @return bool
     * @throws \Throwable
     * @throws \yii\base\ErrorException
     * @throws \yii\db\StaleObjectException
     */
    public function uninstall($extension)
    {
        if (empty($extension->element)) {
            Yii::$app->session->addFlash('warning', Yii::t('installer', 'ERROR_LANG_UNINSTALL_ELEMENT_EMPTY'));
            return false;
        }

        if ($extension->protected === 1) {
            Yii::$app->session->addFlash('warning', Yii::t('installer', 'ERROR_LANG_UNINSTALL_PROTECTED'));
            return false;
        }

        $languagesParams = json_decode(Extensions::findOne(['element' => 'mod_languages'])->params);
        $client = Clients::findOne($extension->client_id);
        if ($languagesParams->{$client->name} === $extension->element) {
            Yii::$app->session->addFlash('warning', Yii::t('installer', 'ERROR_LANG_UNINSTALL_DEFAULT'));
            return false;
        }

        /*
		 * Does this extension have a parent package?
		 * If so, check if the package disallows individual extensions being uninstalled if the package is not being uninstalled
		 */
        if ($extension->package_id && !$this->parent->isPackageUninstall()) {
            Yii::$app->session->addFlash('warning', Yii::t('installer', 'ERROR_CANNOT_UNINSTALL_CHILD_OF_PACKAGE', $extension->name));
            return false;
        }

        $path = Yii::getAlias('@root' . $client->path . '/messages/' . $extension->element);
        $this->parent->setPath('source', $path);
        $this->parent->findManifest();

        if (is_dir($path)) {
            FileHelper::removeDirectory($path);
            if (is_dir($path)) {
                Yii::$app->session->addFlash('warning', Yii::t('installer', 'ERROR_LANG_UNINSTALL_DIRECTORY'));
                return false;
            }
        } else {
            $extension->delete();
            Yii::$app->session->addFlash('warning', Yii::t('installer', 'ERROR_LANG_UNINSTALL_PATH_EMPTY'));
            return false;
        }

        $clientName = Clients::findOne($extension->client_id)->name;
        if ($clientName === Helper::getClient()) {
            Languages::findOne(['code' => $extension->element])->delete();
        }

        $extension->delete();

        return true;
    }

    /**
     * Method to do any prechecks and setup the install paths for the extension
     * @return void
     */
    protected function setupInstallPaths()
    {
        // TODO: Implement setupInstallPaths() method.
    }

    /**
     * Method to copy the extension's base files from the `<files>` tag(s) and the manifest file
     * @return void
     * @throws  \RuntimeException
     */
    protected function copyBaseFiles()
    {
        // TODO: Implement copyBaseFiles() method.
    }

    /**
     * Method to store the extension to the database
     * @return void
     * @throws \RuntimeException
     */
    protected function storeExtension()
    {
        // TODO: Implement storeExtension() method.
    }

    /**
     * Gets a unique language SEF string.
     * @param string $languageTag Language Tag
     * @return string
     */
    private function getSefString($languageTag)
    {
        $langs = explode('-', $languageTag);
        $prefixToFind = $langs[0];

        $language = Languages::findOne(['code' => $languageTag]);
        if (is_null($language)) {
            $language = Languages::findOne(['sef' => $prefixToFind]);
            return is_null($language) ? $prefixToFind : strtolower($languageTag);
        } else {
            return $language->sef;
        }
    }

    /**
     * 更新语言扩展
     * @return bool
     * @throws \Throwable
     * @throws \yii\base\ErrorException
     * @throws \yii\db\Exception
     */
    public function update()
    {
        $clientName = (string)$this->manifest->attributes()->client;
        $client = Clients::findOne(['name' => $clientName]);
        if (is_null($client)) {
            $paramMsg = Yii::t('installer', 'ERROR_UNKNOWN_CLIENT', $clientName);
            $this->parent->abort(Yii::t('installer', 'ABORT_LANGUAGE', $paramMsg));
            return false;
        }
        if (!$this->manifest->tag) {
            $paramMsg = Yii::t('installer', 'ERROR_NO_LANGUAGE_TAG');
            $this->parent->abort(Yii::t('installer', 'ABORT_LANGUAGE', $paramMsg));
            return false;
        }
        // Do we have a meta file in the file list?  In other words... is this a core language pack?
        if (count($this->manifest->files->children())) {
            foreach ($this->manifest->files->children() as $child) {
                if ((string)$child->attributes()->file === 'meta') {
                    $this->core = true;
                    break;
                }
            }
        }
        // Copy all the necessary files
        if ($this->parent->parseFiles($this->manifest->files, $clientName) === false) {
            $this->parent->abort();
            return false;
        }
        $languageExtension = $this->extension->findOne([
            'element' => $this->manifest->tag,
            'type' => 'language',
            'client_id' => $client->id
        ]);
        if (is_null($languageExtension)) {
            $languageExtension->folder = '';
            $languageExtension->enabled = 1;
            $languageExtension->protected = 0;
            $languageExtension->client_id = $client->id;
            $languageExtension->params = $this->parent->getParams();
        }
        $languageExtension->name = trim((string)$this->manifest->name);
        $languageExtension->type = 'language';
        $languageExtension->element = $this->manifest->tag;
        $languageExtension->manifest_cache = $this->parent->generateManifestCache();
        if (!$languageExtension->save()) {
            $errorMsg = implode('&emsp;&emsp;<br>', $languageExtension->getFirstErrors());
            $this->parent->abort(Yii::t('installer', 'ABORT_LANGUAGE', $errorMsg));
            return false;
        }
        return $languageExtension->id;
    }
}
